﻿using System;
using System.Text;
using System.Diagnostics.Contracts;

namespace BitTorrent.Bencode {
    internal class ByteString : Object, IComparable<ByteString>, IEquatable<ByteString> {
        private byte[] _bytes;

        /// <summary>
        /// Initializes a new instance of the ByteString class.
        /// </summary>
        public ByteString() {
        }

        internal ByteString(byte[] data) {
            Contract.Requires(data != null);
            Contract.Requires(data.Length > 0);

            _bytes = data;
        }

        public string String {
            get { 
                return Encoding.UTF8.GetString(_bytes); 
            }
            set {
                Contract.Requires(value != null);

                _bytes = Encoding.UTF8.GetBytes(value); 
            }
        }

        public byte[] Bytes { get { return _bytes; } set { _bytes = value; } }

        public int Length {
            get { return _bytes.Length; }
        }

        internal override int ByteSize {
            get {
                int size = Encoding.UTF8.GetByteCount(string.Format("{0}:", Length));
                return (size + Length);
            }
        }

        internal override int Encode(byte[] data, int offset) {
            Contract.Requires(data != null);
            Contract.Requires(offset > 0);
            Contract.Requires(offset < data.Length);

            string str = string.Format("{0}:", Length);
            int written = Encoding.UTF8.GetBytes(str, 0, str.Length, data, offset);
            Buffer.BlockCopy(_bytes, 0, data, offset + written, Length);

            return Length + written;
        }

        public static bool operator ==(ByteString self, ByteString value) {
            return self.Equals(value);
        }

        public static bool operator !=(ByteString self, ByteString value) {
            return !self.Equals(value);
        }

        public static explicit operator ByteString(string str) {
            return new ByteString(Encoding.UTF8.GetBytes(str));
        }

        public int CompareTo(ByteString other) {
            Contract.Requires(other != null);

            int length = Length > other.Length ? other.Length : Length;
            for (int i = 0; i < length; ++i) {
                if (_bytes[i].CompareTo(other._bytes[i]) == 0) {
                    continue;
                }
                return _bytes[i].CompareTo(other._bytes[i]);
            }

            if (Length > other.Length) {
                return 1;
            }

            if (Length < other.Length) {
                return -1;
            }

            return 0;
        }

        bool IEquatable<ByteString>.Equals(ByteString other) {
            return Equals(other);
        }

        public int CompareTo(string other) {
            return String.CompareTo(other);
        }

        public override int GetHashCode() {
            return _bytes.GetHashCode();
        }

        public override string ToString() {
            return String;
        }

        public override bool Equals(object obj) {
            if (obj == null) {
                return false;
            }

            if (obj is string) {
                return String.Equals((string)obj);
            }

            if (obj is ByteString) {
                return _bytes.Equals(((ByteString)obj).Bytes);
            }

            return false;
        }
    }
}
